package citic.c.util;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class IntervalUtil {

    public static List<Interval> add(Interval n1, Interval n2, BigDecimal step) {
        List<Interval> res = new ArrayList<>();
        Interval.IntervalRelation relation = n1.check(n2, step);
        switch (relation) {
            case same:
                res.add(n1);
                break;
            case separationLess:
                res.add(n1);
                res.add(n2);
                break;
            case separationLessLink:
                Interval n3 = new Interval(n1.getStart(), n2.getEnd());
                res.add(n3);
                break;
            case intersectLess:
                Interval n4 = new Interval(n1.getStart(), n2.getEnd());
                res.add(n4);
                break;
            case includeLess:
                res.add(n1);
                break;
            case includeGreat:
                res.add(n2);
                break;
            case separationGreatLink:
                Interval n5 = new Interval(n2.getStart(), n1.getEnd());
                res.add(n5);
                break;
            case intersectGreat:
                Interval n6 = new Interval(n2.getStart(), n1.getEnd());
                res.add(n6);
                break;
            case separationGreat:
                res.add(n2);
                res.add(n1);
                break;
        }

        return res;
    }

    public static List<Interval> sub(Interval n1, Interval n2, BigDecimal step) {
        List<Interval> res = new ArrayList<>();
        Interval.IntervalRelation relation = n1.check(n2, step);
        switch (relation) {
            case same:
                break;
            case separationLess:
                res.add(n1);
                break;
            case separationLessLink:
                res.add(n1);
                break;
            case intersectLess:
                Interval n4 = new Interval(n1.getStart(), n2.getStart().subtract(step));
                res.add(n4);
                break;
            case includeLess:
                Interval n5 = new Interval(n1.getStart(), n2.getStart().subtract(step));
                Interval n6 = new Interval(n2.getEnd().add(step), n1.getEnd());
                res.add(n5);
                res.add(n6);
                break;
            case includeGreat:
                Interval n7 = new Interval(n2.getStart(), n1.getStart().subtract(step));
                Interval n8 = new Interval(n1.getEnd().add(step), n2.getEnd());
                res.add(n7);
                res.add(n8);
                break;
            case separationGreatLink:
                res.add(n1);
                break;
            case intersectGreat:
                Interval n9 = new Interval(n2.getEnd().add(step), n1.getEnd());
                res.add(n9);
                break;
            case separationGreat:
                res.add(n1);
                break;
        }

        return res;
    }

    public static Boolean checkSeparate(Interval d1, Interval d2, BigDecimal step) {
        if (d1.check(d2, step).equals(Interval.IntervalRelation.separationLess) || d1.check(d2, step).equals(Interval.IntervalRelation.separationGreat)) {
            return true;
        } else {
            return false;
        }
    }

    public static Boolean checkSeparate(List<Interval> dl, BigDecimal step) {
        for (Interval dp1 : dl) {
            for (Interval dp2 : dl) {
                if (dp1 != dp2 && !checkSeparate(dp1, dp2, step)) {
                    return false;
                }
            }
        }
        return true;
    }

    public static List<Interval> add(List<Interval> list, BigDecimal step) {
        List<Interval> res = new ArrayList<>();
        if (list == null) {
            return res;
        }
        if (list.size() == 0) {
            return res;
        }
        if (list.size() == 1) {
            res.addAll(list);
            return res;
        }
        /**
         * 列表都是分离的纠返回
         */
        if (checkSeparate(list, step)) {
            return list;
        } else {
            List<Interval> tdp = new ArrayList<>();
            //for(int i=0;i<dl.size();i++) {
            Boolean isAdd = false;
            List<Interval> newAdd = new ArrayList<>();
            newAdd.add(list.get(0));
            for (int j = 1; j < list.size(); j++) {
                /**
                 * 第一项与后面的比，如果相交，就加在一次,放入列表
                 * 如果不相交，就将被加时间片加入列表
                 */
                if (!checkSeparate(list.get(0), list.get(j), step) && !isAdd) {
                    newAdd = add(list.get(0), list.get(j), step);
                    tdp.addAll(newAdd);
                    isAdd = true;
                } else {
                    tdp.add(list.get(j));
                }
            }
            /**
             * dl.get(0)没有被合并过就将这个节点放入列表最后。
             */
            if (!isAdd) {
                tdp.add(list.get(0));
            }
            /**
             * 递归执行，直到不再有相交时间片
             */
            return add(tdp, step);
        }
    }

    /**
     * 把一个区间加到一个区间列表上去
     * @param dlist
     * @param d2
     * @param step
     * @return
     */
    public static List<Interval> add(List<Interval> dlist, Interval d2, BigDecimal step) {
        List<Interval> res = new ArrayList<>();
        res.addAll(dlist);
        res.add(d2);
        return add(res, step);
    }

    /**
     * 从一个区间列表删除一个区间
     * @param rdl
     * @param d1
     * @param step
     * @return
     */
    public static List<Interval> sub(List<Interval> rdl, Interval d1, BigDecimal step) {
        List<Interval> res = new ArrayList<>();
        for (Interval dp : rdl) {
            if (checkSeparate(dp, d1, step)) {
                /**
                 * 分离的时间片直接保留列表中的项
                 */
                res.add(dp);
            } else {
                /**
                 * 相交的分别减去时间片d1，然后全部加入一个列表
                 */
                List<Interval> tdp = sub(dp, d1, step);
                res.addAll(tdp);
            }
        }
        /**
         * 合并结果
         */
        return add(res,step);
    }

    /**
     * 从一个区间减掉一个区间列表
     * @param d1
     * @param dlist
     * @param step
     * @return
     */
    public static List<Interval> sub(Interval d1, List<Interval> dlist, BigDecimal step) {
        List<Interval> res = new ArrayList<>();
        if (dlist.size() == 0) {
            res.add(d1);
            return res;
        }
        List<Interval> ndl = add(dlist, step);
        res.add(d1);
        for (Interval dp : ndl) {
            res = sub(res, dp, step);
        }
        return res;
    }

    public static void main(String[] args) {
        Interval in1=new Interval(0,100);
        Interval in2=new Interval(80,120);
        Interval in3=new Interval(50,150);

        List<Interval> l1=new ArrayList();
        l1.add(in1);
        l1.add(in2);
        //l1.add(in3);

        List lR1=IntervalUtil.add(l1,in3,new BigDecimal(0));
        List lR2=IntervalUtil.sub(l1,in3,new BigDecimal(0));

        lR1.stream().forEach( o-> System.out.println(o));
        System.out.println("//////////");
        lR2.stream().forEach( o-> System.out.println(o));

        System.out.println("////////////////////////////");

        Interval in1T=new Interval(new Date().getTime()-10000000,new Date().getTime()-2000);
        Interval in2T=new Interval(new Date().getTime()-10000000,new Date().getTime()-2000);
        Interval in3T=new Interval(new Date().getTime()-10000000,new Date().getTime()-2000);

        List<Interval> l1T=new ArrayList();
        l1T.add(in1T);
        l1T.add(in2T);
        //l1.add(in3);

        List lR1T=IntervalUtil.add(l1T,in3T,new BigDecimal(0));
        List lR2T=IntervalUtil.sub(l1T,in3T,new BigDecimal(0));

        lR1T.stream().forEach( o-> System.out.println(o));
        System.out.println("//////////");
        lR2T.stream().forEach( o-> System.out.println(o));




    }
}
